package com.scx.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.scx.constant.MessageConstant;
import com.scx.constant.StatusConstant;
import com.scx.dto.BookDTO;
import com.scx.dto.BookPageQueryDTO;
import com.scx.entity.Book;
import com.scx.entity.BookGifts;
import com.scx.entity.SetmealBook;
import com.scx.exception.DeletionNotAllowedException;
import com.scx.mapper.BookGiftsMapper;
import com.scx.mapper.BookMapper;
import com.scx.mapper.SetmealBookMapper;
import com.scx.result.PageResult;
import com.scx.service.BookService;
import com.scx.utils.CacheClient;
import com.scx.vo.BookVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.scx.constant.RedisConstants.*;
import static com.scx.constant.RedisConstants.CACHE_BOOK_TTL;

@Service
@Slf4j
public class BookServiceImpl implements BookService {
    @Autowired
    private BookMapper bookMapper;
    @Autowired
    private BookGiftsMapper bookGiftsMapper;
    @Autowired
    private SetmealBookMapper setmealBookMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private CacheClient cacheClient;

    @Override
    public void saveWithGifts(BookDTO bookDTO) {
        Book book = new Book();
        BeanUtils.copyProperties(bookDTO, book);
        bookMapper.insert(book);

        Long bookId = book.getId();
        List<BookGifts> gifts = bookDTO.getGifts();

        if (gifts != null && gifts.size() > 0) {
            for (BookGifts df : gifts) {
                df.setBookId(bookId);
                bookGiftsMapper.insert(df);
            }
        }
    }

    @Override
    public PageResult pageQuery(BookPageQueryDTO bookPageQueryDTO) {
        //对于多表操作，使用原生的mybatis直接编写sql更加灵活
        PageHelper.startPage(bookPageQueryDTO.getPage(), bookPageQueryDTO.getPageSize());
        Page<BookVO> page = bookMapper.pageQuery(bookPageQueryDTO);
        return new PageResult(page.getTotal(), page.getResult());
    }

    @Override
    @Transactional
    public void deleteBatch(List<Long> ids) {
        //判断是否存在起售中的图书
        LambdaQueryWrapper<Book> lqw1 = new LambdaQueryWrapper<>();
        lqw1.in(Book::getId, ids);
        List<Book> bookes = bookMapper.selectList(lqw1);
        bookes.forEach(book -> {
            if (Objects.equals(book.getStatus(), StatusConstant.ENABLE)) {
                throw new DeletionNotAllowedException(MessageConstant.Book_ON_SALE);
            }
        });

        //判断是否被套餐关联
        LambdaQueryWrapper<SetmealBook> lqw2 = new LambdaQueryWrapper<>();
        lqw2.in(SetmealBook::getBookId, ids);
        List<SetmealBook> setmealBookes = setmealBookMapper.selectList(lqw2);
        if (setmealBookes != null && setmealBookes.size() > 0) {
            throw new DeletionNotAllowedException(MessageConstant.Book_BE_RELATED_BY_SETMEAL);
        }

        //批量删除图书
        bookMapper.deleteBatchIds(ids);
        //批量删除图书对应礼品
        LambdaQueryWrapper<BookGifts> lqw3 = new LambdaQueryWrapper<>();
        lqw3.in(BookGifts::getBookId,ids);
        bookGiftsMapper.delete(lqw3);
    }

    @Override
    public BookVO getByIdWithGifts(Long id) {
        Book book = bookMapper.selectById(id);

        LambdaQueryWrapper<BookGifts> lqw = new LambdaQueryWrapper<>();
        lqw.eq(BookGifts::getBookId, id);
        List<BookGifts> bookGifts = bookGiftsMapper.selectList(lqw);

        BookVO bookVO = new BookVO();
        BeanUtils.copyProperties(book,bookVO);
        bookVO.setGifts(bookGifts);

        return bookVO;
    }

    @Override
    public void updateWithGifts(BookDTO bookDTO) {
        Book book = new Book();
        BeanUtils.copyProperties(bookDTO,book);

        bookMapper.updateById(book);

        //重新插入数据
        LambdaQueryWrapper<BookGifts> lqw = new LambdaQueryWrapper<>();
        lqw.eq(BookGifts::getBookId, bookDTO.getId());
        bookGiftsMapper.delete(lqw);

        List<BookGifts> gifts = bookDTO.getGifts();
        if (gifts != null && gifts.size() > 0) {
            for (BookGifts df : gifts) {
                df.setBookId(bookDTO.getId());
                bookGiftsMapper.insert(df);
            }
        }
    }

    @Override
    public List<Book> list(Long categoryId) {
        LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Book::getCategoryId, categoryId);
        lqw.eq(Book::getStatus, StatusConstant.ENABLE);
        return bookMapper.selectList(lqw);
    }

    @Override
    public void startOrStop(Integer status, Long id) {
        Book book = Book.builder()
                .status(status)
                .id(id)
                .build();
        bookMapper.updateById(book);
    }

    @Override
    public List<BookVO> listWithGifts(Long categoryIdh) {
        List<Book> bookList = list(categoryIdh);

        List<BookVO> bookVOList = new ArrayList<>();

        for (Book book : bookList) {
            BookVO bookVO = new BookVO();
            BeanUtils.copyProperties(book,bookVO);

            LambdaQueryWrapper<BookGifts> lqw = new LambdaQueryWrapper<>();
            lqw.eq(BookGifts::getBookId, book.getId());
            List<BookGifts> bookGifts = bookGiftsMapper.selectList(lqw);
            bookVO.setGifts(bookGifts);
            bookVOList.add(bookVO);
        }
        return bookVOList;
    }

    @Override
    public List<Book> top10() {
        Set<String> bookIds = redisTemplate.opsForZSet().reverseRange("top_book", 0, 9);
        if(CollUtil.isEmpty(bookIds)){
            return Collections.emptyList();
        }
        List<Book> books = bookMapper.selectBatchIds(bookIds);
        return books;
    }

    @Override
    public Book queryById(Long id) {
        //使用封装工具类
        // 解决缓存穿透
        Book book = cacheClient
                .queryWithPassThrough(CACHE_BOOK_KEY, id, Book.class, this::getById, CACHE_BOOK_TTL, TimeUnit.MINUTES);

//         //互斥锁解决缓存击穿
//        Book book = cacheClient
//                 .queryWithMutex(CACHE_BOOK_KEY, id, Book.class, this::getById, CACHE_BOOK_TTL, TimeUnit.MINUTES);
//
//         //逻辑过期解决缓存击穿
//        Book book = cacheClient
//                 .queryWithLogicalExpire(CACHE_BOOK_KEY, id, Book.class, this::getById, 20L, TimeUnit.SECONDS);

        return book;
    }

    private Book getById(Long id){
        return bookMapper.selectById(id);
    }
}
